﻿#include "study07.h"

// C/C++ 学习网站：https://legacy.cplusplus.com 注意旧版本（搜索）和新版本
// C/C++ 微软(Visual Studio)中文学习网站：https://learn.microsoft.com/zh-cn/cpp/cpp/?view=msvc-170

/*
宏：是一种预编译指令，它在预编译阶段执行，并将其替换为代码。更具体的语法参见本人 C 语言学习笔记。
	宏的使用主要是为了适配不同平台、提高效率、简化代码、提高可读性。
	这里写一个在 Debug 模式下打印日志而不在 Release 模式下打印日志的例子。
		首先需要添加宏命定义：
			项目名右键->属性->C/C++->预处理器->预处理定义->在 Debug 模式下添加自己的宏定义，
				如：MyDebug，可以赋值，因为这会被当做 #define 语句处理
			同样在 Release 模式下也添加自己的宏定义，如：MyRelease (可在 <不同选项> 前面添加，注意使用分号)

auto：是C++11引入的新关键字，它可以自动推导变量的类型。
	auto 使用时，编译器会根据初始化表达式自动推导出变量的类型，并将其赋给变量。
	auto 主要用于声明变量，不能用于函数参数。
	一个建议：短类型不要用 auto，特别长的类型才用 auto 但也不要肆意使用。

vector 的迭代器 iterator 是一个类模板，它可以用来遍历 vector 中的元素;
	vector<type>::iterator.begin() 返回指向第一个元素的迭代器，
	vector::iterator.end() 返回指向最后一个元素的迭代器。

unordered_map 容器的使用：
	unordered_map<key_type, value_type> my_map[ = { {key1, value1}, {key2, value2},... }];
		其中，key_type 和 value_type 是模板参数，表示 unordered_map 的键值对类型。
		这里的 key_type 和 value_type 可以是任意类型，但必须是可哈希的。
		key_type 必须是 const 类型，因为 unordered_map 的查找操作是通过哈希表实现的。

	my_map[key] = value; // 插入或修改元素
	value = my_map[key]; // 获取元素, 如果不存在则返回默认值
	my_map.at(key); // 获取元素, 如果不存在则抛出异常
	my_map.push_back({key, value}); // 插入元素
	my_map.erase(key); // 删除元素，效率较慢
	my_map.find(key); // 查找元素是否存在
	my_map.size(); // 获取元素个数
	my_map.clear(); // 清空容器
	for (auto it = my_map.begin(); it != my_map.end(); ++it) // 遍历容器

在 C++ 标准库中有一个 array 类模板，它可以用来声明固定大小的数组。
	std::array<type, size> my_array[ = {value1, value2, ...}];
		其中，type 是数组元素的类型，size 是数组的大小。
		这里的 type 可以是任意类型，但必须是可复制的。
		array 类支持标准库模版各种算法，因为它支持随机访问迭代器。
		array.size()  获取的数组大小（元素个数）实质是模版 template 的实现，所以在运行时才会生成。

使用函数指针时，通常可以和 auto 关键字使用，例如：
	void (*func_ptr)(int, int) = my_func; // 声明函数指针
	auto func_ptr2 = my_func; // 声明函数指针，使用 auto 推导类型

C++11 语法中 lambda 表达式可以用来创建匿名函数，可以简化代码，其实际是一种函数指针。
	语法：
		[capture](parameters)mutable->return_type{function_body}
		其中，capture 部分可以省略，输入 = 表示捕获所有变量值传递，输入 & 表示捕获所有变量引用，
			输入 this 表示捕获 this 指针。
		mutable 部分表示函数是否可以修改 this 指针，即加上该关键字就可以修改捕获到的变量值。
		return_type 部分可以省略，表示函数的返回值类型。
		function_body 部分是函数体。
	使用 lambda 表达式可以简化代码，例如：
		auto func_lambda = [](int a, int b) -> int {
			cout << "my_func(" << a << ", " << b << ")" << endl;
			return a + b;
		};
	在 <algorithm> 库中，有个 find_if 函数可以用来查找满足条件的元素，其参数为一个函数指针，可以写为 lambda 表达式。
		find_if 支持迭代器范围和函数指针，可以用 lambda 表达式简化代码，例如：
		find_if(my_array.begin(), my_array.end(), [](int value) { return value > 5; });

命名空间：是一种组织代码的方式，它可以帮助避免命名冲突。
	通过使用 using namespace std; 可以做到其作用域的任何地方都可以使用 std 命名空间中的内容，不带 std:: 前缀。
	但是建议不要滥用命名空间，因为它会使代码难以维护，而且会引入不必要的复杂性；尤其在头文件中需要特别注意。
	单独声明命名空间的一个成员时，需要在命名空间成员名前加上 :: 符号，语法：using 命名空间名::成员名;
	此外，命名空间支持嵌套使用，可以将不同功能的命名空间分割开来，避免命名冲突；但是函数内不允许定义新的命名空间。
	给命名空间其别名的语法：namespace 别名 = 原命名空间名;
*/
static void print_array(const array<int, 10>& my_array) { // 定义函数，参数为固定大小的数组
	for (int i = 0; i < my_array.size(); ++i)
		cout << my_array[i] << " ";
	cout << endl;
}

static void my_func(int a, int b) { // 定义函数，参数为 int 类型
	cout << "my_func(" << a << ", " << b << ")" << endl;
}

static void time_log() {
	time_t time_now = time(0); // 获取当前时间
	cout << "now time is " << ctime(&time_now);// ctime 需要定义宏 _CRT_SECURE_NO_WARNINGS
}

static void print_value(int value, void(* const func_ptr)()) {
	cout << "value is " << value << endl;
	func_ptr(); // 调用函数指针
}

static void for_each(const std::vector<int>& arr, const std::function<int(int)>& func) {
	for (int value : arr) // 使用 range-based for 遍历数组
		cout << func(value) << endl;
}

int main()
{
	// 宏与 auto 关键字测试
	test_macro_auto();

	// 静态数组测试
	print_start("静态数组测试");
	array<int, 10> my_array = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	print_array(my_array);
	print_end("静态数组测试");

	// 函数指针测试
	{
		print_start("函数指针测试");
		typedef void(*FuncPtr)(int, int); // 定义函数指针类型
		void (*fp)(int a, int b) = &my_func; // 声明函数指针，指向 my_func 地址，类型为 void(*)(int, int)
		//其中 a 和 b 可以省略，因为函数名本身就是指针，所以可以直接赋值给函数指针，所以取地址 & 符号可以省略。
		(*fp)(1, 2); // 解引用 fp 指针，调用 my_func 函数，输出 "my_func(1, 2)"
		fp(10, 20); // 解引用 * 运算符同样可以省略。
		auto fp_copy = my_func; // 可以看到 fp_copy 类型为 void(*)(int, int)，和 fp 类型一致。
		(*fp_copy)(3, 4);
		FuncPtr t_def = my_func;
		t_def(5, 6); // 也可以使用函数指针类型定义变量，并赋值为函数地址。
		// 函数指针实现日志：功能函数调用一次打印一次调用时间
		/*const auto fpt = time_log; // const 修饰函数指针，防止修改指向的地址。
		for (int i = 0; i < 5; ++i) {
			print_value(i + 1, fpt); // 调用 print_value 函数，传入 i 和 fpt 指针
			Sleep(1000); // 休眠 1 秒，需要包含 windows.h 头文件。
		} */
		// 上面的功能使用 lambda 表达式也可以实现：
		print_value(10, []() -> void {
			time_t time_now = time(0); // 获取当前时间
			cout << "now time is " << ctime(&time_now);// ctime 需要定义宏 _CRT_SECURE_NO_WARNINGS
		});

		print_end("函数指针测试");
	}

	// lambda 表达式测试
	{
		print_start("lambda表达式测试");
		std::vector<int> my_array2 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
		int len = 5;
		auto func_lambda = [&](int value) mutable -> int {
			static bool is_changed = true;
			if (is_changed) {
				len = 10; // 尝试修改外部变量，只修改一次
				is_changed = false;
			}
			cout << "lambda_func(" << value << ") is ";
			return value;
			}; // 这里只是定义，没有调用
		cout << "len is " << len << endl; // 输出 5
		for_each(my_array2, func_lambda); // 调用 for_each 函数，传入 my_array2 和 func_lambda 指针，在 for_each 函数里面调用 func_lambda
		cout << "len is " << len << endl; // 输出 10
		auto ret1 = std::find_if(my_array2.begin(), my_array2.end(),
			[&](int value) -> bool { return value > 5; }); // 使用 lambda 表达式查找大于 5 的第一个元素
		cout << "find_if result is " << *ret1 << endl;
		
		print_end("lambda表达式测试");
	}

	// 命名空间测试
	test_namespace();

	system("pause"); // 等待用户输入
	return 0;
}